﻿/*                       NOTICE
 * 
 * This software was produced for the U. S. Government
 * under Contract No. FA8721-10-C-0001, and is
 * subject to the Rights in Noncommercial Computer Software
 * and Noncommercial Computer Software Documentation Clause
 * (DFARS) 252.227-7014 (JUN 1995)
 * 
 * Approved for Public Release: 11-2435
 * © 2011 The MITRE Corporation. All Rights Reserved. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GRIP
{
    public class IDStateHolder<T>
    {
        public List<T> NewIDs = new List<T>();
        public List<T> ExistingIDs = new List<T>();
        public List<T> RemovedIDs = new List<T>();
        public List<T> InactiveIDs = new List<T>();

        public void Clear()
        {
            NewIDs = new List<T>();
            ExistingIDs = new List<T>();
            RemovedIDs = new List<T>();
            InactiveIDs = new List<T>();
        }
    }

    public class BufferFilter<T>
    {
        public int EntryBufferTime = 200 * 10000;
        public int ExitBufferTime = 200 * 10000;
        protected Dictionary<T, bool> activeIDs = new Dictionary<T, bool>();
        protected Dictionary<T, long> startTimes = new Dictionary<T, long>();
        protected List<T> vanishingIDs = new List<T>();
        protected List<T> previousIDs = new List<T>();
        protected SortedIDHolder<T> sortedIDHolder = new SortedIDHolder<T>();
        protected IDStateHolder<T> idStateHolder = new IDStateHolder<T>();

        public BufferFilter()
        {
        }

        public BufferFilter(int EntryBufferTime, int ExitBufferTime)
        {
            this.EntryBufferTime = EntryBufferTime * 10000;
            this.ExitBufferTime = ExitBufferTime * 10000;
        }

        public IDStateHolder<T> process(IEnumerable<T> incomingIDs)
        {
            idStateHolder.Clear();

            classifyIDs(incomingIDs);
            processNewIDs(sortedIDHolder.newIDs);
            processPendingIDs(sortedIDHolder.pendingIDs);
            processRemovedIDs(sortedIDHolder.removedIDs);
            processVanishingIDs();
            previousIDs = new List<T>(incomingIDs);

            return idStateHolder;
        }

        protected void processNewIDs(IEnumerable<T> newIDs)
        {
            foreach (T newID in newIDs)
            {
                if (vanishingIDs.Contains(newID))
                {
                    vanishingIDs.Remove(newID);
                    idStateHolder.ExistingIDs.Add(newID);
                }
                else
                {
                    startTimes[newID] = DateTime.Now.Ticks;
                    activeIDs[newID] = false;
                    idStateHolder.InactiveIDs.Add(newID);
                }
            }
        }

        protected void processPendingIDs(IEnumerable<T> pendingIDs)
        {
            foreach (T pendingID in pendingIDs)
            {
                if (activeIDs[pendingID])
                    idStateHolder.ExistingIDs.Add(pendingID);
                else if (DateTime.Now.Ticks - startTimes[pendingID] > EntryBufferTime)
                {
                    activeIDs[pendingID] = true;
                    idStateHolder.NewIDs.Add(pendingID);
                }
                else
                    idStateHolder.InactiveIDs.Add(pendingID);
            }
        }

        protected void processRemovedIDs(IEnumerable<T> removedIDs)
        {
            foreach (T removedID in removedIDs)
            {
                if (activeIDs[removedID])
                {
                    vanishingIDs.Add(removedID);
                    startTimes[removedID] = DateTime.Now.Ticks;
                }
                else
                    idStateHolder.InactiveIDs.Add(removedID);
            }
        }

        protected void processVanishingIDs()
        {
            for (int i = 0; i < vanishingIDs.Count; i++)
            {
                T vanishingID = vanishingIDs[i];
                if (DateTime.Now.Ticks - startTimes[vanishingID] > ExitBufferTime)
                {
                    activeIDs[vanishingID] = false;
                    vanishingIDs.RemoveAt(i);
                    i--;
                    idStateHolder.RemovedIDs.Add(vanishingID);
                }
                else
                    idStateHolder.ExistingIDs.Add(vanishingID);
            }
        }

        protected void classifyIDs(IEnumerable<T> incomingIDs)
        {
            sortedIDHolder.newIDs = incomingIDs.Except(previousIDs);
            sortedIDHolder.pendingIDs = incomingIDs.Intersect(previousIDs);
            sortedIDHolder.removedIDs = previousIDs.Except(incomingIDs);
        }

        protected class SortedIDHolder<U>
        {
            public IEnumerable<U> newIDs;
            public IEnumerable<U> pendingIDs;
            public IEnumerable<U> removedIDs;
        }
    }
}
